package com.k8.common.factroy.instance;

import com.k8.common.factroy.BeanInfo;
import com.k8.common.factroy.BeanRegistry;
import com.k8.common.ioc.BeanPostProcessor;
import com.k8.common.ioc.Injector;
import com.k8.common.ioc.Required;
import com.k8.common.ioc.injectorImpl.SafeInjectorHolder;
import com.k8.common.ioc.injectorImpl.SafeObjectHolder;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
public abstract class AbstractBeanInstanceStrategyFactory implements InstanceStrategyFactory {
    public final static String FIRST_BEAN_SUFFIX = "#" + 1;
    private final static String MID_FIX = "#";
    @Setter
    protected List<BeanPostProcessor> beanPostProcessors;
    Map<Class<?>, AtomicInteger> classCount = new ConcurrentHashMap<>(20);
    Map<String, BeanInfo<?>> cacheInstance = new ConcurrentHashMap<>(20);
    protected Injector injector;
    private SafeInjectorHolder safeInjectorHolder;

    /**
     * 用户实例化最后的依赖注入检查
     */
    @Override
    public BeanInfo getBeanInfo(String relyBeanName) {
        return this.cacheInstance.get(relyBeanName);
    }

    @Override
    public void setInjector(Injector injector) {
        this.injector = injector;
        this.safeInjectorHolder = new SafeInjectorHolder(injector);
    }

    private int getClassCount(Class clazz) {
        AtomicInteger atomicInteger = this.classCount.get(clazz);
        if (atomicInteger == null) return -1;
        return atomicInteger.get();
    }

    @Override
    public <T> List<T> getAllBeans(Class<T> tClass) {
        if (tClass.isInterface()) return null;
        int classCount = getClassCount(tClass);
        String beanName = tClass.getName();
        List<T> result = new ArrayList<>(classCount);
        for (int i = 1; i < classCount; ++i) {
            T bean = getBean(tClass, beanName + MID_FIX + classCount);
            //有可能该bean正在创建，虽然有计数器值
            if (bean != null) result.add(bean);
        }
        return result;
    }

    public void registryBean(Object instance) {
        Class clazz = instance.getClass();
        String simpleName = clazz.getName();
        String beanName = simpleName + MID_FIX + nextCount(clazz);
        registryBean(instance, beanName);
    }

    // registry，注册一个bean，若name重复则报错，不提供name则根据类型创建，+#+count,count从1开始，0留给依赖注入的使用
    public void registryBean(Object instance, String name) {
        if (name == null) throw new IllegalArgumentException("beanName is null");
        Class clazz = instance.getClass();
        registryBean(instance, clazz, name);
    }

    private <T> void registryBean(T instance, Class<T> clazz, String name) {
        cacheInstance.computeIfAbsent(name, key -> {
            return new BeanInfo<T>(clazz);
        });
        BeanInfo<?> beanInfo = cacheInstance.get(name);
        synchronized (beanInfo) { //有可能有同名实例正在创建
            if (beanInfo.state() != 1 || beanInfo.getValue() != null) {
                throw new RuntimeException("This name bean is exist");
            }
            verifyClass(clazz, beanInfo);
            BeanInfo<T> beanInfo1 = (BeanInfo<T>) beanInfo;
            beanInfo1.setValue(instance);
            beanInfo1.state(4);
        }
    }

    private int nextCount(Class clazz) {
        AtomicInteger atomicInteger = classCount.get(clazz);
        if (atomicInteger == null) {
            atomicInteger = classCount.computeIfAbsent(clazz, key -> new AtomicInteger(0));
        }
        return atomicInteger.incrementAndGet();
    }

    //count从1开始，0留给依赖注入
    public <T> T getOrRegistryBean(Class<T> clazz) {
        if (clazz.isInterface()) throw new IllegalArgumentException("Class is a interface, class: " + clazz.getName());
        String name = clazz.getName() + MID_FIX + nextCount(clazz);
        return getOrRegistryBean(clazz, name);
    }

    /*
     * A依赖于B，B依赖于A
     * A注入B时，B已经在实例化了，A被阻塞等待B实例化结束，B注入A时，A也已经在实例化了，B阻塞等待A实例化成功，死锁
     * 允许A获取没有完全实例化成功的B，但是AOP的介入，故此必须在只让同一线程对一个对象实例化的同时，允许其他线程提前曝光该实例
     * 提前曝光故此两种情况发生
     * 1.同一线程循环依赖发生时会提前曝光
     * 2.其它线程循环依赖时尝试获取该实例时
     * 但是必须禁止非循环依赖的情况下其他线程获取到没有完全处理完的实例
     * */
    //循环依赖再次获取bean的入口应该和普通获取bean的入口不同,区分处理获取bean和处理依赖注入两个事件
    //同一时刻只允许有一个线程在实例化bean使用双检查实现
    public <T> T getOrRegistryBean(Class<T> clazz, String name) {
        if (clazz.isInterface()) throw new IllegalArgumentException("Class is a interface, class: " + clazz.getName());
        if (name == null || name.isEmpty()) throw new IllegalArgumentException("Name is illegal, name: " + name);
        BeanInfo<?> beanInfo = cacheInstance.get(name);
        if (beanInfo == null) {
            cacheInstance.computeIfAbsent(name, key -> {
                return new BeanInfo<>(clazz);
            });
            beanInfo = cacheInstance.get(name);
            verifyClass(clazz, beanInfo);
            BeanInfo<T> beanInfo1 = (BeanInfo<T>) beanInfo;
            synchronized (beanInfo) {
                if (beanInfo1.state() == -1) {
                    throw new RuntimeException("This bean cannot instance class: " + clazz.getName());
                } else if (beanInfo1.getValue() == null) {
                    createBean(beanInfo1, clazz, name);
                }
            }
            return (T) cacheInstance.get(name).getValue();
        } else if (beanInfo.state() == 4) {
            if (beanInfo.getValue() == null) {
                throw new IllegalStateException("BeanInfo state is not equals state of instance, class: " + clazz.getName() + " beanName: " + name);
            }
            verifyClass(clazz, beanInfo);
            return (T) beanInfo.getValue();
        } else {
            synchronized (beanInfo) {
                if (beanInfo.state() != 4 || beanInfo.getValue() == null) {
                    throw new IllegalStateException("BeanInfo state is not equals state of instance, class: " + clazz.getName() + " beanName: " + name);
                }
                verifyClass(clazz, beanInfo);
                return (T) beanInfo.getValue();
            }
        }
    }


    /**
     * getBean不会创建Bean，只会获取,若不指定名字则按类型获取第一个
     */
    public <T> T getBean(Class<T> clazz, String name) {
        if (clazz.isInterface()) throw new IllegalArgumentException("Class is a interface, class: " + clazz.getName());
        BeanInfo<?> beanInfo = cacheInstance.get(name);
        if (beanInfo != null && beanInfo.getType().equals(clazz) && beanInfo.state() == 4)
            return (T) beanInfo.getValue();
        return null;
    }

    public <T> T getBean(Class<T> clazz) {
        if (clazz.isInterface()) throw new IllegalArgumentException("Class is a interface, class: " + clazz.getName());
        String beanName = clazz.getName() + FIRST_BEAN_SUFFIX;
        return getBean(clazz, beanName);
    }

    public <E> E createProtoBean(Class<E> clazz) {
        E instance = newInstance(clazz);
        Map<String, Object> relyBeanMaps = populateExtension(instance);
        instance = doPostProcessor(instance, clazz.getName());
        doAfterPostProcessor(instance, clazz.getName());
        if (!(instance instanceof Injector)) {
            this.injector.synJudge(relyBeanMaps);
        }
        return instance;
    }

    /*
     * 先尝试实例化bean
     * 若失败，修改标志位为-1，抛出异常，若成功则继续后续的操作，依赖注入
     * 依赖注入后可能bean已经被提前曝光处理，状态标志位为2，则不需要再后续处理，直接修改状态标志位为3
     * */
    private <T> void createBean(BeanInfo<T> beanInfo, Class<T> clazz, String name) {
        try {
            T instance = null;
            try {
                instance = newInstance(clazz);
            } catch (RuntimeException e) {
                beanInfo.state(-1);
                throw new RuntimeException(e);
            }
            beanInfo.setValue(instance);
            Map<String, Object> relyBeanMaps = populateExtension(instance);

            AtomicInteger state = beanInfo.getState();
            synchronized (state) {
                if (state.get() == 1) {
                    try {
                        instance = doPostProcessor(instance, name);
                    } catch (RuntimeException e) {
                        beanInfo.state(-1);
                        throw new RuntimeException(e);
                    }
                    beanInfo.setValue(instance);
                    doAfterPostProcessor(instance, name);
                } else if (state.get() == 2) {
                    doAfterPostProcessor(instance, name);
                    beanInfo.setValue(instance);
                }
                state.set(3);

                //阻塞判定依赖注入的属性是否实例化好了
                if (!(instance instanceof Injector)) {
                    this.injector.synJudge(relyBeanMaps);
                }
                state.set(4);
            }
        } catch (RuntimeException e) {
            beanInfo.state(-1);
            throw new BeanInstanceException(e);
        }

    }

    /**
     * 暴露的接口，用于扩展,但是此时已经instance是最终状态了，不能再修改instance了,比如提供afterProperties等操作
     */
    protected abstract <T> void doAfterPostProcessor(T instance, String name);

    protected abstract <T> T doPostProcessor(T instance, String name);

    /**
     * 如果不提供无参构造则会报错
     */
    private <T> T newInstance(Class<T> clazz) {
        Constructor<?>[] constructors = clazz.getConstructors();
        for (Constructor<?> constructor : constructors) {
            try {
                Object[] args = new Object[constructor.getParameters().length];
                return (T) constructor.newInstance(args);
            } catch (InvocationTargetException | InstantiationException |
                     IllegalAccessException e) {
            }
        }
        throw new IllegalStateException("This class cannot instance");
    }

    /**
     * 有ToInject后缀的方法只有在依赖注入时才调用，为了区分正常调用
     */
    public <T> T getBeanOrRegistryToInject(Class<T> clazz) {
        if (clazz.isInterface()) throw new IllegalArgumentException("Class is a interface, class: " + clazz.getName());
        String name = clazz.getName() + FIRST_BEAN_SUFFIX;
        return getBeanOrRegistryToInject(clazz, name);
    }

    public <T> T getBeanOrRegistryToInject(Class<T> clazz, String name) {
        if (clazz.isInterface()) throw new IllegalArgumentException("Class is a interface, class: " + clazz.getName());
        if (name == null || name.isEmpty()) throw new IllegalArgumentException("Name is illegal, name: " + name);
        BeanInfo<?> beanInfo = cacheInstance.get(name);
        if (beanInfo == null) {
            cacheInstance.computeIfAbsent(name, key -> new BeanInfo<>(clazz));
            beanInfo = cacheInstance.get(name);
            synchronized (beanInfo) {
                verifyClass(clazz, beanInfo);
                BeanInfo<T> beanInfo1 = (BeanInfo<T>) beanInfo;
                if (beanInfo1.state() == -1) {
                    throw new RuntimeException("This bean cannot instance class: " + clazz.getName());
                } else if (beanInfo1.getValue() == null) {
                    createBean(beanInfo1, clazz, name);
                }
            }
            return (T) cacheInstance.get(name).getValue();
        } else if (beanInfo.state() == 3 || beanInfo.state() == 2 || beanInfo.state() == 4) {
            verifyClass(clazz, beanInfo);
            if (beanInfo.getValue() == null) {
                throw new IllegalStateException("BeanInfo state is not equals state of instance in inject time, class: " + clazz.getName() + " beanName: " + name);
            }
            return (T) beanInfo.getValue();
        } else if (beanInfo.state() == 1) {
            verifyClass(clazz, beanInfo);
            BeanInfo<T> beanInfo1 = (BeanInfo<T>) beanInfo;
            return (T) earlyExposureBean(beanInfo1, name, clazz);
        }
        throw new BeanInstanceException("State of rely instance is error(-1)");
    }

    private void verifyClass(Class clazz1, BeanInfo beanInfo) {
        if (clazz1 == null) {
            throw new IllegalArgumentException("clazz1 must not null");
        }
        Class type = beanInfo.getType();
        if (!clazz1.equals(type)) {
            throw new IllegalStateException("This name does not match class, class: " + clazz1.getName());
        }
    }

    /**
     * 需要通过循环等待该bean对象创建好
     */
    private <T> T earlyExposureBean(BeanInfo<T> beanInfo, String name, Class<T> clazz) {
        T instance = beanInfo.getValue();
        while (instance == null && beanInfo.state() != -1) {//等待bean实例化好,使用cas的方式，不要再加锁
            instance = beanInfo.getValue();
        }
        if (beanInfo.state() == -1) {
            throw new IllegalStateException("This bean cannot instance class: " + clazz.getName());
        }
        AtomicInteger state = beanInfo.getState();
        synchronized (state) {
            if (state.get() == 1) {
                try {
                    instance = doPostProcessor(instance, name);
                } catch (RuntimeException e) {
                    beanInfo.state(-1);
                    throw new RuntimeException(e);
                }
                beanInfo.setValue(instance);
                state.set(2);
            }
        }
        return (T) beanInfo.getValue();
    }

    /**
     * 需要根据返回值map最后阻塞判断是否所有注入的属性状态都为3,第二种类型的injector和tempMap是因为BeanRegistry需要暴露injector和map给用户
     * 为了安全性需要一个安全持有者来保证安全
     */
    private Map<String, Object> populateExtension(Object instance) {

        Map<String, Object> relyBeanMaps = new TreeMap<>();

        if (this.injector != null) {
            Class<?> clazz = instance.getClass();
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                if (injector.needInject(declaredField)) {
                    if (this.injector.inject(relyBeanMaps, instance, declaredField)) {
                        continue;
                    }
                    Required annotation = declaredField.getAnnotation(Required.class);
                    if (annotation == null || annotation.require()) {
                        throw new IllegalArgumentException("This bean cannot found. class: " + declaredField.getType().getName());
                    }
                }
            }
        } else if (!(instance instanceof Injector)) {
            log.error("injector is not registry");
        }

        Map<String, SafeObjectHolder> tempRelyBeanMaps = null;
        if (instance instanceof BeanRegistry beanRegistry) {
            tempRelyBeanMaps = new TreeMap<>();
            beanRegistry.injectBean(this.safeInjectorHolder, tempRelyBeanMaps);
        }
        if (tempRelyBeanMaps != null && tempRelyBeanMaps.size() != 0) {
            tempRelyBeanMaps.forEach((key, holder) -> {
                relyBeanMaps.put(key, holder.get());
            });
        }
        return relyBeanMaps;
    }


}
